﻿// *********************************************************************
// [DCOM Productions]
// [Copyright (C) DCOM Productions All rights reserved.]
// *********************************************************************

namespace BitFlex.Windows.Forms.CustomUI {
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Drawing;
    using System.Drawing.Drawing2D;

    /// <summary>
    /// Helper class used to render the visual elements of a UIProgressBar
    /// </summary>
    public sealed class UIProgressBarRenderer {
        /// <summary>
        /// Instantiates a new instance of the BitFlex.Windows.Forms.UIProgressBarRenderer class.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="graphics"></param>
        public UIProgressBarRenderer(UIProgressBar control, Graphics graphics) {
            m_Control = control;
            m_Graphics = graphics;
        }

        #region Properties

        private float m_BorderTension = 0.75F;
        /// <summary>
        /// The tension to use when drawing a rounded border
        /// </summary>
        public float BorderTension {
            get {
                return m_BorderTension;
            }
        }

        private UIProgressBar m_Control;
        /// <summary>
        /// The default control to draw to
        /// </summary>
        public UIProgressBar Control {
            get {
                return m_Control;
            }
        }

        private Graphics m_Graphics;
        /// <summary>
        /// The default graphics object to use
        /// </summary>
        public Graphics Graphics {
            get {
                return m_Graphics;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Draws the standard UIProgressBar background
        /// </summary>
        /// <param name="g"></param>
        public void DrawBackground() {
            using (LinearGradientBrush brush = new LinearGradientBrush(m_Control.ClientRectangle, m_Control.BackColorOne, m_Control.BackColorTwo, m_Control.BackColorAngle)) {
                m_Graphics.FillRectangle(brush, m_Control.ClientRectangle);
            }
        }

        private void DrawBar() {
            if (Control.Value == 0) {
                return;
            }

            if (Control.Style == UIProgressBarStyles.Blocks) {
                DrawBlocks();
            }
            else {
                float percent = (float)(Control.Value - Control.Minimum) / (float)(Control.Maximum - Control.Minimum);

                Rectangle rect = Control.ClientRectangle;
                rect.X++;
                rect.X += (int)Control.BarPadding;
                rect.Y++;
                rect.Y += (int)Control.BarPadding;
                rect.Height -= 2;
                rect.Height -= (int)(Control.BarPadding * 2);
                rect.Width -= 2;
                rect.Width = (int)((float)rect.Width * percent) - (int)(Control.BarPadding * 2);

                using (LinearGradientBrush brush = new LinearGradientBrush(Control.ClientRectangle, Control.BarColorOne, Control.BarColorTwo, Control.BarColorAngle)) {
                    Graphics.FillRectangle(brush, rect);
                }

                if (Control.Style == UIProgressBarStyles.SmoothBlocks) {
                    DrawSmoothBlocks();
                }
            }
        }

        private void DrawBlocks() {
            if (Control.Value == 0) {
                return;
            }

            float percent = (float)(Control.Value - Control.Minimum) / (float)(Control.Maximum - Control.Minimum);
            float blockStop = ((float)Control.ClientRectangle.Width * percent) - Control.BlockWidth - Control.BlockSpacing;
            RectangleF rectf = new RectangleF();
            rectf.Width = Control.BlockWidth;
            rectf.Height = (Control.Height - 2) - (Control.BarPadding * 2);
            rectf.X = 1 + Control.BarPadding;
            rectf.Y = 1 + Control.BarPadding;

            using (LinearGradientBrush brush = new LinearGradientBrush(Control.ClientRectangle, Control.BarColorOne, Control.BarColorTwo, Control.BarColorAngle)) {
                while (rectf.X < blockStop) {
                    Graphics.FillRectangle(brush, rectf);
                    rectf.X += Control.BlockWidth + Control.BlockSpacing;
                }

                while ((rectf.X + rectf.Width) > Control.Width - Control.BarPadding - 1) {
                    rectf.Width--;
                }

                Graphics.FillRectangle(brush, rectf);
            }
        }

        /// <summary>
        /// Draws a standard squared border on a UIProgressBar
        /// </summary>
        public void DrawBorder() {
            switch (Control.BorderStyle) {
                case UIProgressBarBorderStyles.Rounded:
                    if (Control.Enabled) {
                        DrawRoundedBorder(m_Control.BorderColor);
                    }
                    else {
                        DrawRoundedBorder(SystemColors.GrayText);
                    }
                    break;
                case UIProgressBarBorderStyles.None:
                    // No draw
                    break;
                default:
                    if (Control.Enabled) {
                        DrawSquaredBorder(m_Control.BorderColor);
                    }
                    else {
                        DrawSquaredBorder(SystemColors.GrayText);
                    }
                    break;
            }
        }

        private void DrawGlassOverlay() {
            if (Control.Style == UIProgressBarStyles.Blocks) {
                float percent = (float)(Control.Value - Control.Minimum) / (float)(Control.Maximum - Control.Minimum);
                float blockStop = ((float)Control.ClientRectangle.Width * percent) - Control.BlockWidth - Control.BlockSpacing;
                RectangleF rectf = new RectangleF();
                rectf.Width = Control.BlockWidth;
                rectf.Height = (Control.Height / 2) - Control.BarPadding;
                rectf.X = 1 + Control.BarPadding;
                rectf.Y = 1 + Control.BarPadding;

                if (Control.GlassOverlaySpring) {
                    rectf.X = 0;
                    rectf.Y = 0;
                    rectf.Height = Control.ClientRectangle.Height / 2;
                    rectf.Width = Control.Width;

                    using (SolidBrush brush = new SolidBrush(Color.FromArgb(Control.GlassOverlayAlpha, Color.White))) {
                        Graphics.FillRectangle(brush, rectf);
                    }
                }
                else {
                    if (Control.Value == 0) {
                        return;
                    }

                    using (SolidBrush brush = new SolidBrush(Color.FromArgb(Control.GlassOverlayAlpha, Color.White))) {
                        while (rectf.X < blockStop) {
                            Graphics.FillRectangle(brush, rectf);
                            rectf.X += Control.BlockWidth + Control.BlockSpacing;
                        }

                        while ((rectf.X + rectf.Width) > Control.Width - Control.BarPadding - 1) {
                            rectf.Width--;
                        }

                        Graphics.FillRectangle(brush, rectf);
                    }
                }
            }
            else {
                float percent = (float)(Control.Value - Control.Minimum) / (float)(Control.Maximum - Control.Minimum);
                Rectangle rect = Control.ClientRectangle;

                if (Control.GlassOverlaySpring) {
                    rect.Width = Control.Width;
                }
                else {
                    rect.Width = (int)((float)rect.Width * percent);
                }

                rect.Height /= 2;

                using (SolidBrush brush = new SolidBrush(Color.FromArgb(Control.GlassOverlayAlpha, Color.White))) {
                    Graphics.FillRectangle(brush, rect);
                }
            }
        }

        private void DrawMarqueeBar(Rectangle rect) {
            using (LinearGradientBrush brush = new LinearGradientBrush(rect, Control.BarColorOne, Control.BarColorTwo, Control.BarColorAngle)) {
                Graphics.FillRectangle(brush, rect);
            }
        }

        private void DrawMarqueeBlocks(Rectangle rect) {
            RectangleF rectf = new RectangleF();
            rectf.Height = rect.Height;
            rectf.Width = Control.BlockWidth;
            rectf.X = rect.X;
            rectf.Y = rect.Y;

            using (LinearGradientBrush brush = new LinearGradientBrush(rectf, Control.BarColorOne, Control.BarColorTwo, Control.BackColorAngle)) {
                while (rectf.X < (rect.X + rect.Width)) {
                    Graphics.FillRectangle(brush, rectf);
                    rectf.X += Control.BlockSpacing + Control.BlockWidth;
                }
            }
        }

        private void DrawMarqueeGlassOverlay(Rectangle rect) {
            if (Control.GlassOverlaySpring) {
                RectangleF rectf = new RectangleF();
                rectf.Height = Control.Height / 2;
                rectf.Width = Control.Width;
                rectf.X = 0;
                rectf.Y = 0;

                using (SolidBrush brush = new SolidBrush(Color.FromArgb(Control.GlassOverlayAlpha, Color.White))) {
                    Graphics.FillRectangle(brush, rectf);
                }
            }
            else {
                if (Control.MarqueeStyle == UIProgressBarMarqueeStyles.Blocks) {
                    RectangleF rectf = new RectangleF();
                    rectf.Height = rect.Height / 2;
                    rectf.Width = Control.BlockWidth;
                    rectf.X = rect.X;
                    rectf.Y = rect.Y;

                    using (SolidBrush brush = new SolidBrush(Color.FromArgb(Control.GlassOverlayAlpha, Color.White))) {
                        while (rectf.X < (rect.X + rect.Width)) {
                            Graphics.FillRectangle(brush, rectf);
                            rectf.X += Control.BlockSpacing + Control.BlockWidth;
                        }
                    }
                }
                else {
                    RectangleF rectf = new RectangleF();
                    rectf.Height = rect.Height / 2;
                    rectf.Width = rect.Width;
                    rectf.X = rect.X;
                    rectf.Y = rect.Y;

                    using (SolidBrush brush = new SolidBrush(Color.FromArgb(Control.GlassOverlayAlpha, Color.White))) {
                        Graphics.FillRectangle(brush, rectf);
                    }
                }
            }
        }

        public void DrawProgress() {
            if (Control.UseSystemRenderer) {
                if (!ProgressBarRenderer.IsSupported) {
                    Control.UseSystemRenderer = false;
                }
                else {
                    if (Control.UseSystemVertical) {
                        DrawSystemStyleVertical();
                    }
                    else {
                        DrawSystemStyle();
                    }
                    return;
                }
            }

            if (!Control.Enabled) {
                return;
            }

            if (Control.Style == UIProgressBarStyles.Marquee) {
                switch (Control.MarqueeStyle) {
                    case UIProgressBarMarqueeStyles.Blocks:
                        DrawMarqueeBlocks(Control.MarqueeRectangle);
                        break;
                    default:
                        DrawMarqueeBar(Control.MarqueeRectangle);
                        break;
                }

                if (Control.GlassOverlay) {
                    DrawMarqueeGlassOverlay(Control.MarqueeRectangle);
                }
            }
            else {
                DrawBar();

                if (Control.GlassOverlay) {
                    DrawGlassOverlay();
                }

                if (Control.EnableStatusText) {
                    DrawStatusText();
                }
            }
        }
        
        /// <summary>
        /// Draws a rounded border on a UIProgressBar
        /// </summary>
        private void DrawRoundedBorder(Color color) {
            Point[] borderCurveBottomLeft = 
            { 
                new Point(0, m_Control.ClientRectangle.Height - 5), 
                new Point(0, m_Control.ClientRectangle.Height - 3), 
                new Point(2, m_Control.ClientRectangle.Height - 1), 
                new Point(4, m_Control.ClientRectangle.Height - 1) };

            Point[] borderCurveBottomRight = 
            { 
                new Point(m_Control.ClientRectangle.Width - 1, m_Control.ClientRectangle.Height - 5), 
                new Point(m_Control.ClientRectangle.Width - 1, m_Control.ClientRectangle.Height - 3), 
                new Point(m_Control.ClientRectangle.Width - 3, m_Control.ClientRectangle.Height - 1), 
                new Point(m_Control.ClientRectangle.Width - 5, m_Control.ClientRectangle.Height - 1) };

            Point[] borderCurveTopLeft = 
            { 
                new Point(0, 4), 
                new Point(0, 2), 
                new Point(2, 0), 
                new Point(4, 0) };

            Point[] borderCurveTopRight = 
            { 
                new Point(m_Control.ClientRectangle.Width - 5, 0), 
                new Point(m_Control.ClientRectangle.Width - 3, 0), 
                new Point(m_Control.ClientRectangle.Width - 1, 2), 
                new Point(m_Control.ClientRectangle.Width - 1, 4) };

            using (Pen pen = new Pen(new SolidBrush(color), 1.0F)) {
                m_Graphics.DrawCurve(pen, borderCurveTopLeft, m_BorderTension);
                m_Graphics.DrawCurve(pen, borderCurveTopRight, m_BorderTension);
                m_Graphics.DrawCurve(pen, borderCurveBottomRight, m_BorderTension);
                m_Graphics.DrawCurve(pen, borderCurveBottomLeft, m_BorderTension);
                m_Graphics.DrawLine(pen, 4, 0, m_Control.ClientRectangle.Width - 5, 0);
                m_Graphics.DrawLine(pen, m_Control.ClientRectangle.Width - 1, 4, m_Control.ClientRectangle.Width - 1, m_Control.ClientRectangle.Height - 5);
                m_Graphics.DrawLine(pen, 4, m_Control.ClientRectangle.Height - 1, m_Control.ClientRectangle.Width - 5, m_Control.ClientRectangle.Height - 1);
                m_Graphics.DrawLine(pen, 0, 4, 0, m_Control.ClientRectangle.Height - 5);
            }

            Color cornerColor = Color.Transparent;

            if (m_Control.Parent != null) {
                cornerColor = m_Control.Parent.BackColor;
            }

            using (SolidBrush brush = new SolidBrush(cornerColor)) {
                m_Graphics.FillRectangle(brush, 0, 1, 1, 1);
                m_Graphics.FillRectangle(brush, 0, 0, 1, 1);
                m_Graphics.FillRectangle(brush, 1, 0, 1, 1);

                m_Graphics.FillRectangle(brush, m_Control.ClientRectangle.Width - 2, 0, 1, 1);
                m_Graphics.FillRectangle(brush, m_Control.ClientRectangle.Width - 1, 0, 1, 1);
                m_Graphics.FillRectangle(brush, m_Control.ClientRectangle.Width - 1, 1, 1, 1);

                m_Graphics.FillRectangle(brush, m_Control.ClientRectangle.Width - 1, m_Control.ClientRectangle.Height - 2, 1, 1);
                m_Graphics.FillRectangle(brush, m_Control.ClientRectangle.Width - 1, m_Control.ClientRectangle.Height - 1, 1, 1);
                m_Graphics.FillRectangle(brush, m_Control.ClientRectangle.Width - 2, m_Control.ClientRectangle.Height - 1, 1, 1);

                m_Graphics.FillRectangle(brush, 1, m_Control.ClientRectangle.Height - 1, 1, 1);
                m_Graphics.FillRectangle(brush, 0, m_Control.ClientRectangle.Height - 1, 1, 1);
                m_Graphics.FillRectangle(brush, 0, m_Control.ClientRectangle.Height - 2, 1, 1);
            }
        }

        private void DrawSmoothBlocks() {
            if (Control.Value == 0) {
                return;
            }

            float percent = (float)(Control.Value - Control.Minimum) / (float)(Control.Maximum - Control.Minimum);
            float blockStop = ((float)Control.ClientRectangle.Width * percent) - Control.BlockWidth - Control.BlockSpacing;

            RectangleF rectf = new RectangleF();
            rectf.Width = Control.BlockSpacing;
            rectf.Height = Control.Height - 1;
            rectf.X = 1;
            rectf.Y = 0;

            using (LinearGradientBrush brush = new LinearGradientBrush(rectf, Control.BackColorOne, Control.BackColorTwo, Control.BackColorAngle)) {
                while (rectf.X < blockStop) {
                    rectf.X += Control.BlockWidth + Control.BlockSpacing;
                    Graphics.FillRectangle(brush, rectf);
                }
            }
        }

        /// <summary>
        /// Draws the squared border on a UIProgressBar
        /// </summary>
        private void DrawSquaredBorder(Color color) {
            using (Pen pen = new Pen(new SolidBrush(color), 1.0F)) {
                m_Graphics.DrawLine(pen, 0, 0, m_Control.ClientRectangle.Width, 0);
                m_Graphics.DrawLine(pen, m_Control.ClientRectangle.Width - 1, 0, m_Control.ClientRectangle.Width - 1, m_Control.ClientRectangle.Height - 1);
                m_Graphics.DrawLine(pen, 0, m_Control.ClientRectangle.Height - 1, m_Control.ClientRectangle.Width - 1, m_Control.ClientRectangle.Height - 1);
                m_Graphics.DrawLine(pen, 0, 0, 0, m_Control.ClientRectangle.Height - 1);
            }
        }

        private void DrawStatusText() {
            if (!Control.EnableStatusText) {
                return;
            }

            float y = (float)(Control.Height / 2) - (float)(Graphics.MeasureString(Control.StatusText, Control.StatusFont).Height / 2);
            float x = 0;

            switch (Control.StatusAlignment) {
                case HorizontalAlignment.Center:
                    x = Control.Width - (float)(Control.Width / 2) - (float)(Graphics.MeasureString(Control.StatusText, Control.StatusFont).Width / 2);
                    break;
                case HorizontalAlignment.Left:
                    x = 5;
                    break;
                case HorizontalAlignment.Right:
                    x = (float)(Control.Width - 6) - (float)Graphics.MeasureString(Control.StatusText, Control.StatusFont).Width;
                    break;
            }

            Graphics.DrawString(Control.StatusText, Control.StatusFont, new SolidBrush(Control.StatusTextColor), x, y);
        }

        private void DrawSystemStyle() {
            float percent = (float)(Control.Value - Control.Minimum) / (float)(Control.Maximum - Control.Minimum);

            Rectangle rect = Control.ClientRectangle;
            rect.Width = (int)((float)rect.Width * percent) - 2;
            rect.Height = Control.Height - 2;
            rect.X = 1;
            rect.Y = 1;

            ProgressBarRenderer.DrawHorizontalBar(Graphics, Control.ClientRectangle);

            if (!Control.Enabled) {
                return;
            }

            ProgressBarRenderer.DrawHorizontalChunks(Graphics, rect);
        }

        private void DrawSystemStyleVertical() {
            float percent = (float)(Control.Value - Control.Minimum) / (float)(Control.Maximum - Control.Minimum);

            Rectangle rect = Control.ClientRectangle;
            rect.Width = Control.Width - 2;
            rect.Height = (int)((float)rect.Height * percent) - 2;
            rect.X = 1;
            rect.Y = Control.ClientRectangle.Height - rect.Height - 1;

            ProgressBarRenderer.DrawVerticalBar(Graphics, Control.ClientRectangle);

            if (!Control.Enabled) {
                return;
            }

            ProgressBarRenderer.DrawVerticalChunks(Graphics, rect);
        }

        #endregion
    }
}
